package com.mini.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.*;
import org.springframework.util.CollectionUtils;
import redis.clients.jedis.JedisCommands;

import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * DES : redis 缓存工具类
 * 2017/10/9 11:08
 * CREATE BY zhangxiaobin
 */

@Slf4j
public class RedisCacheUtils {
    private static boolean flag = true;//缓存开关

    private static StringRedisTemplate stringRedisTemplate = SpringContextHolder.getBean("stringRedisTemplate");
    private static RedisTemplate<String, Object> redisTemplate = SpringContextHolder.getBean("redisTemplate");

    /**
     * 删除缓存<br>
     * 根据key精确匹配删除
     *
     * @param key
     */
    @SuppressWarnings("unchecked")
    public static void del(String... key) {
        if (flag && key != null && key.length > 0) {
            if (key.length == 1) {
                redisTemplate.delete(key[0]);
            } else {
                redisTemplate.delete(CollectionUtils.arrayToList(key));
            }
        }
    }

    /**
     * 批量删除<br>
     * （该操作会执行模糊查询，请尽量不要使用，以免影响性能或误删）
     *
     * @param pattern
     */
    public static void batchDel(String... pattern) {
        if (flag) {
            for (String kp : pattern) {
                redisTemplate.delete(redisTemplate.keys(kp + "*"));
            }
        }
    }

    /**
     * 取得缓存（int型）
     *
     * @param key
     * @return
     */
    public static Integer getInt(String key) {
        if (flag) {
            String value = stringRedisTemplate.boundValueOps(key).get();
            if (StringUtils.isNotBlank(value)) {
                return Integer.valueOf(value);
            }
        }

        return null;
    }

    /**
     * 取得缓存（int型）
     *
     * @param key
     * @return
     */
    public static Long getLong(String key) {
        if (flag) {
            String value = stringRedisTemplate.boundValueOps(key).get();
            if (StringUtils.isNotBlank(value)) {
                return Long.valueOf(value);
            }
        }
        return null;
    }

    /**
     * 取得缓存（字符串类型）
     *
     * @param key
     * @return
     */
    public static String getStr(String key) {
        if (flag) {
            return stringRedisTemplate.boundValueOps(key).get();
        } else {
            return null;
        }
    }

    /**
     * 取得缓存（字符串类型）
     *
     * @param key
     * @return
     */
    public static String getStr(String key, boolean retain) {
        if (flag) {
            String value = stringRedisTemplate.boundValueOps(key).get();
            if (!retain) {
                redisTemplate.delete(key);
            }
            return value;
        }
        return null;
    }

    /**
     * 获取缓存<br>
     * 注：基本数据类型(Character除外)，请直接使用get(String key, Class<T> clazz)取值
     *
     * @param key
     * @return
     */
    public static Object getObj(String key) {
        if (flag) {
            return redisTemplate.boundValueOps(key).get();
        }
        return null;
    }

    /**
     * 获取缓存<br>
     * 注：java 8种基本类型的数据请直接使用get(String key, Class<T> clazz)取值
     *
     * @param key
     * @param retain 是否保留
     * @return
     */
    public static Object getObj(String key, boolean retain) {
        if (flag) {
            Object obj = redisTemplate.boundValueOps(key).get();
            if (!retain) {
                redisTemplate.delete(key);
            }
            return obj;
        } else {
            return null;
        }
    }

    /**
     * 获取缓存<br>
     * 注：该方法暂不支持Character数据类型
     *
     * @param key   key
     * @param clazz 类型
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> T get(String key, Class<T> clazz) {
        return (T) redisTemplate.boundValueOps(key).get();
    }

    /**
     * 获取缓存json对象<br>
     *
     * @param key   key
     * @param clazz 类型
     * @return
     */
    public static <T> T getJson(String key, Class<T> clazz) {
        if (flag) {
            return JsonMapper.fromJsonString(stringRedisTemplate.boundValueOps(key).get(), clazz);
        } else {
            return null;
        }
    }

    /**
     * 将value对象写入缓存
     *
     * @param key
     * @param value
     * @param time  失效时间(秒)
     */
    public static void set(String key, Object value, ExpireTime time) {
        if (flag) {
            if (value.getClass().equals(String.class)) {
                stringRedisTemplate.opsForValue().set(key, value.toString());
            } else if (value.getClass().equals(Integer.class)) {
                stringRedisTemplate.opsForValue().set(key, value.toString());
            } else if (value.getClass().equals(Double.class)) {
                stringRedisTemplate.opsForValue().set(key, value.toString());
            } else if (value.getClass().equals(Float.class)) {
                stringRedisTemplate.opsForValue().set(key, value.toString());
            } else if (value.getClass().equals(Short.class)) {
                stringRedisTemplate.opsForValue().set(key, value.toString());
            } else if (value.getClass().equals(Long.class)) {
                stringRedisTemplate.opsForValue().set(key, value.toString());
            } else if (value.getClass().equals(Boolean.class)) {
                stringRedisTemplate.opsForValue().set(key, value.toString());
            } else {
                redisTemplate.opsForValue().set(key, value);
            }
            if (time.getTime() > 0) {
                redisTemplate.expire(key, time.getTime(), TimeUnit.SECONDS);
            }
        }
    }

    /**
     * 将value对象写入缓存
     *
     * @param key
     * @param value
     * @param time  失效时间(秒)
     */
    public static void set(String key, Object value, int time) {
        if (flag) {
            if (value.getClass().equals(String.class)) {
                stringRedisTemplate.opsForValue().set(key, value.toString());
            } else if (value.getClass().equals(Integer.class)) {
                stringRedisTemplate.opsForValue().set(key, value.toString());
            } else if (value.getClass().equals(Double.class)) {
                stringRedisTemplate.opsForValue().set(key, value.toString());
            } else if (value.getClass().equals(Float.class)) {
                stringRedisTemplate.opsForValue().set(key, value.toString());
            } else if (value.getClass().equals(Short.class)) {
                stringRedisTemplate.opsForValue().set(key, value.toString());
            } else if (value.getClass().equals(Long.class)) {
                stringRedisTemplate.opsForValue().set(key, value.toString());
            } else if (value.getClass().equals(Boolean.class)) {
                stringRedisTemplate.opsForValue().set(key, value.toString());
            } else {
                redisTemplate.opsForValue().set(key, value);
            }
            if (time > 0) {
                redisTemplate.expire(key, time, TimeUnit.SECONDS);
            }
        }
    }


    /**
     * 将value对象以JSON格式写入缓存
     *
     * @param key
     * @param value
     * @param time  失效时间(秒)
     */
    public static void setJson(String key, Object value, ExpireTime time) {
        if (flag) {
            stringRedisTemplate.opsForValue().set(key, JsonMapper.toJsonString(value));
            if (time.getTime() > 0) {
                stringRedisTemplate.expire(key, time.getTime(), TimeUnit.SECONDS);
            }
        }
    }

    public static boolean setIfAbsent(String key, Object value, ExpireTime time){
        boolean result = false;
        if(flag) {
            if (value.getClass().equals(String.class)) {
                result = stringRedisTemplate.opsForValue().setIfAbsent(key, value.toString());
            } else if (value.getClass().equals(Integer.class)) {
                result = stringRedisTemplate.opsForValue().setIfAbsent(key, value.toString());
            } else if (value.getClass().equals(Double.class)) {
                result = stringRedisTemplate.opsForValue().setIfAbsent(key, value.toString());
            } else if (value.getClass().equals(Float.class)) {
                result = stringRedisTemplate.opsForValue().setIfAbsent(key, value.toString());
            } else if (value.getClass().equals(Short.class)) {
                result = stringRedisTemplate.opsForValue().setIfAbsent(key, value.toString());
            } else if (value.getClass().equals(Long.class)) {
                result = stringRedisTemplate.opsForValue().setIfAbsent(key, value.toString());
            } else if (value.getClass().equals(Boolean.class)) {
                result = stringRedisTemplate.opsForValue().setIfAbsent(key, value.toString());
            } else {
                result = redisTemplate.opsForValue().setIfAbsent(key, value);
            }
            if (time.getTime() > 0) {
                redisTemplate.expire(key, time.getTime(), TimeUnit.SECONDS);
            }
        }
        return result;
    }

    /**
     * 更新key对象field的值
     *
     * @param key   缓存key
     * @param field 缓存对象field
     * @param value 缓存对象field值
     */
    public static void setJsonField(String key, String field, String value) {
        if (flag) {
            JSONObject obj = JSON.parseObject(stringRedisTemplate.boundValueOps(key).get());
            obj.put(field, value);
            stringRedisTemplate.opsForValue().set(key, obj.toJSONString());
        }
    }


    /**
     * 递减操作
     *
     * @param key
     * @param by
     * @return
     */
    public static Double decr(String key, double by) {
        if (flag) {
            return redisTemplate.opsForValue().increment(key, -by);
        } else {
            return null;
        }
    }

    /**
     * 递增操作
     *
     * @param key
     * @param by
     * @return
     */
    public static Double incr(String key, double by) {
        if (flag) {
            return redisTemplate.opsForValue().increment(key, by);
        }
        return null;
    }

    /**
     * 递增操作
     * @param key
     * @param by
     * @return
     */
    public static Long incr(String key, long by){
        if(flag) {
            return stringRedisTemplate.opsForValue().increment(key, by);
        }
        return null;
    }

    /**
     * 获取double类型值
     *
     * @param key
     * @return
     */
    public static Double getDouble(String key) {
        if (flag) {
            String value = stringRedisTemplate.boundValueOps(key).get();
            if (StringUtils.isNotBlank(value)) {
                return Double.valueOf(value);
            }
            return 0d;
        } else {
            return null;
        }
    }

    /**
     * 设置double类型值
     *
     * @param key
     * @param value
     * @param time  失效时间(秒)
     */
    public static void setDouble(String key, double value, ExpireTime time) {
        if (flag) {
            stringRedisTemplate.opsForValue().set(key, String.valueOf(value));
            if (time.getTime() > 0) {
                stringRedisTemplate.expire(key, time.getTime(), TimeUnit.SECONDS);
            }
        }
    }

    /**
     * 设置double类型值
     *
     * @param key
     * @param value
     * @param time  失效时间(秒)
     */
    public static void setInt(String key, int value, ExpireTime time) {
        if (flag) {
            stringRedisTemplate.opsForValue().set(key, String.valueOf(value));
            if (time.getTime() > 0) {
                stringRedisTemplate.expire(key, time.getTime(), TimeUnit.SECONDS);
            }
        }
    }

    /**
     * 设置double类型值
     *
     * @param key
     * @param value
     */
    public static void setInt(String key, int value) {
        if (flag) {
            stringRedisTemplate.opsForValue().set(key, String.valueOf(value),-1);
        }
    }

    /**
     * 将map写入缓存
     *
     * @param key
     * @param map
     * @param time 失效时间(秒)
     */
    public static <T> void setMap(String key, Map<String, T> map, ExpireTime time) {
        if (flag) {
            redisTemplate.opsForHash().putAll(key, map);
        }
    }

    /**
     * 将map写入缓存
     *
     * @param key
     * @param obj
     * @param time 失效时间(秒)
     */
    @SuppressWarnings("unchecked")
    public static <T> void setMap(String key, T obj, ExpireTime time) {
        if (flag) {
            Map<String, String> map = (Map<String, String>) JsonMapper.parseObject(obj, Map.class);
            redisTemplate.opsForHash().putAll(key, map);
        }
    }


    /**
     * 向key对应的map中添加缓存对象
     *
     * @param key
     * @param map
     */
    public static <T> void addMap(String key, Map<String, T> map) {
        if (flag) {
            redisTemplate.opsForHash().putAll(key, map);
        }
    }

    /**
     * 向key对应的map中添加缓存对象
     *
     * @param key   cache对象key
     * @param field map对应的key
     * @param value 值
     */
    public static void addMap(String key, String field, String value) {
        if (flag) {
            redisTemplate.opsForHash().put(key, field, value);
        }
    }

    /**
     * 向key对应的map中添加缓存对象
     *
     * @param key   cache对象key
     * @param field map对应的key
     * @param obj   对象
     */
    public static <T> void addMap(String key, String field, T obj) {
        if (flag) {
            redisTemplate.opsForHash().put(key, field, obj);
        }
    }

    /**
     * 获取map缓存
     *
     * @param key
     * @param clazz
     * @return
     */
    public static <T> Map<String, T> mget(String key, Class<T> clazz) {
        if (flag) {
            BoundHashOperations<String, String, T> boundHashOperations = redisTemplate.boundHashOps(key);
            return boundHashOperations.entries();
        } else {
            return null;
        }
    }

    /**
     * 获取map缓存
     *
     * @param key
     * @param clazz
     * @return
     */
    public static <T> T getMap(String key, Class<T> clazz) {
        if (flag) {
            BoundHashOperations<String, String, String> boundHashOperations = redisTemplate.boundHashOps(key);
            Map<String, String> map = boundHashOperations.entries();
            return JsonMapper.parseObject(map, clazz);
        } else {
            return null;
        }
    }

    /**
     * 获取map缓存中的某个对象
     *
     * @param key
     * @param field
     * @param clazz
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> T getMapField(String key, String field, Class<T> clazz) {
        if (flag) {
            return (T) redisTemplate.boundHashOps(key).get(field);
        } else {
            return null;
        }
    }

    /**
     * 删除map中的某个对象
     *
     * @param key   map对应的key
     * @param field map中该对象的key
     * @author lh
     * @date 2016年8月10日
     */
    public void delMapField(String key, String... field) {
        if (flag) {
            BoundHashOperations<String, String, ?> boundHashOperations = redisTemplate.boundHashOps(key);
            boundHashOperations.delete(field);
        }
    }

    /**
     * 指定缓存的失效时间
     *
     * @param key  缓存KEY
     * @param time 失效时间(秒)
     * @author FangJun
     * @date 2016年8月14日
     */
    public static void expire(String key, ExpireTime time) {
        if (flag) {
            if (time.getTime() > 0) {
                redisTemplate.expire(key, time.getTime(), TimeUnit.SECONDS);
            }
        }
    }

    /**
     * 添加set
     *
     * @param key
     * @param value
     */
    public static void sadd(String key, String... value) {
        if (flag) {
            redisTemplate.boundSetOps(key).add(value);
        }
    }

    /**
     * 删除set集合中的对象
     *
     * @param key
     * @param value
     */
    public static void srem(String key, String... value) {
        if (flag) {
            redisTemplate.boundSetOps(key).remove(value);
        }
    }

    /**
     * set重命名
     *
     * @param oldkey
     * @param newkey
     */
    public static void srename(String oldkey, String newkey) {
        if (flag) {
            redisTemplate.boundSetOps(oldkey).rename(newkey);
        }
    }

    /**
     * 短信缓存
     *
     * @param key
     * @param value
     * @param time
     * @author fxl
     * @date 2016年9月11日
     */
    public static void setIntForPhone(String key, Object value, int time) {
        if (flag) {
            stringRedisTemplate.opsForValue().set(key, JsonMapper.toJsonString(value));
            if (time > 0) {
                stringRedisTemplate.expire(key, time, TimeUnit.SECONDS);
            }
        }
    }

    /**
     * 模糊查询keys
     *
     * @param pattern
     * @return
     */
    public static Set<String> keys(String pattern) {
        if (flag) {
            return redisTemplate.keys(pattern+"*");
        } else {
            return null;
        }
    }

    /**
     * 根据key模糊删除缓存数据
     *
     * @param prefix
     */
    public static void releaseAllLocks(String prefix) {

        Object o = redisTemplate.execute((RedisConnection redisConnection) -> {
            ScanOptions so = ScanOptions.scanOptions().match(prefix + "*").build();
            Cursor<byte[]> cursor = redisConnection.scan(so);
            while (cursor.hasNext()) {
                String key = new String(cursor.next());
                try {
                    redisTemplate.delete(key);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            return null;
        });
    }

    public static boolean trylock(String key) {
        try {
            String result = (String) redisTemplate.execute((RedisCallback<String>) connection -> {
                JedisCommands commands = (JedisCommands) connection.getNativeConnection();
                return commands.set(key, key, "NX", "EX", 5);
            });
            return !StringUtils.isEmpty(result);
        } catch (Exception e) {
            log.error("getNewUsePushLock error", e);
        }
        return false;

    }

    public static void unlock(String key) {
        redisTemplate.delete(key);
    }

}
